// MainFrame.cs
//
//  Copyright (C) 2008 Jacek Trubłajewicz
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Threading;
using System.IO;
using System.Diagnostics;
using Mono.Unix;
using HAKGERSoft;
using NLog;

namespace wxGadu
{
	/// <summary>
	///  Those are fake window IDs, we use it only for handling EVT_UPDATE_UI events
	///  when we need to update widget from thread diffrent then main thread. 
	/// </summary>
	/// <remarks>
	/// To prevent calling event handling method on normal UI updates, we use those fake IDs, 
	/// so method will be called only when we need it and manually add event request by AddPendingEvent().
	/// </remarks>
	public enum FakeId
	{
		statusBitmap = 6000,
		contactListCtrl, 
		contactAdded,
		contactListClear,
		contactListFontsChanged,
		infoPanelFontsChanged,
		infoPanelColorChanged,
		contactListColorChanged,
		connectionTimeoutMessageDialog,
		emptyGaduSettingsMessageDialog,
		clearDescriptionCombo,
		deleteDescriptionsFileDialog,
		tooltipSettingsChanged,
		mainWindowSizeChanged,
		chatWindowSizeChanged,
		trayIconSettingsChanged,
		setTrayIconBitmap,
		clearInfoPanel,
		chatFrameRenderMessages,
		chatFrameRequestUserAttention,
		showNewChatNotify,
		closeNewChatNotify,
		clearChatWindow,
		showNotConnectedDialog,
		showStatusChangeNotify,
		fillHistoryTree,
		scrollMessageWindow
	}
	
	public class MainFrame : wx.Frame 
	{
		#region Id
		private enum Id
		{
			splitterWindow,
			gaduMenu,
			toolsMenu,
			helpMenu,
			newChatContextMenu,
			contactInfoContextMenu,
			contactDeleteContextMenu,
			chatHistoryContextMenu,
			showDescriptionContextMenu,
			settingsMenu,
			newContactMenu,
			newAccountMenu,
			changeEmailMenu,
			changePasswordMenu,
			deleteAccountMenu,
			closeMenu,
			historyMenu
		}
		#endregion
		
		#region windows
		private wx.SplitterWindow splitterWindow;
		private TopMainFramePanel topWindow;
		private BottomMainFramePanel bottomWindow;
		private HistoryFrame histFrame;
		#endregion

		#region controls
		private wx.BoxSizer topSizer;
		private wx.MenuBar menuBar;
		private wx.Menu gaduMenu;
		private wx.Menu toolsMenu;
		private wx.Menu helpMenu;
		private wx.Menu listContexMenu;
		private GaduTaskBarIcon trayIcon;
		#endregion

		#region data members
		private Dictionary<int, ContactInfo> contactList;
		private Dispatcher dispatcher;
		private wx.ImageList statusImages;
		
		private Dictionary<int, wx.Bitmap> statusBitmaps;
		public event UserStatusChangeEventHandler UserStatusChange;
		private wx.Bitmap statusBitmapIcon;
		private Queue<GGUser> outdatedUsers;
		private Queue<GGUser> addedUsers;
#if LOG
		private Logger logger = LogManager.GetCurrentClassLogger();
#endif
		/// <summary>
		/// Keeps UIN of contact on behalf of which context menu was invoked
		/// </summary>
		private int contexMenuUin;
		/// <summary>
		/// Keeps pointers to opened ContactInfoDialog with contact UIN as key
		/// so we are able to check if ContactInfoDialog for specified contact
		/// has already been opened
		/// </summary>
		private Dictionary<int, ContactInfoDialog> openedContactInfos;
		private AppConfig appCfg;
		private ImageManager imageManager;
		private Mutex trayIconMutex;
		/// <summary>
		/// Uin of contact for which status change notification should
		/// be displayed
		/// </summary>
		private ContactStatusChangeData statusChangeNotifyData;
		private Mutex statusChangNotifyMutex;
		private bool realClose;
		#endregion
		
		public MainFrame(wx.Window parent, int id, string title, Point pos, Size size, uint style, AppConfig appConfiguration, ImageManager imanager):
			base(parent, id, title, pos, size, style)
		{
			appCfg = appConfiguration;
			imageManager = imanager;
			trayIconMutex = new Mutex();
			statusChangNotifyMutex = new Mutex();
		
			DoControls();
			DoControlsProperties();
			DoLayout();
			
			// list of users which status need to be updated
			outdatedUsers = new Queue<GGUser>();
			// list of users added to Gadu.Users 
			addedUsers = new Queue<GGUser>();
			
			dispatcher = new Dispatcher(this);	
			openedContactInfos = new Dictionary<int, ContactInfoDialog>();
			// create tray icon
			if (appCfg.UserConf.EnableTrayIcon)
				trayIcon = new GaduTaskBarIcon(this);

									
			#region gui events
			EVT_CLOSE(new wx.EventListener(OnMainFrameClose));
			// context menu for contact list
			EVT_MENU((int) Id.newChatContextMenu, new wx.EventListener(OnNewChatContextMenu));
			EVT_MENU((int) Id.contactInfoContextMenu, new wx.EventListener(OnContactInfoContextMenu));
			EVT_MENU((int) Id.contactDeleteContextMenu, new wx.EventListener(OnContactDeleteContextMenu));
			EVT_MENU((int) Id.chatHistoryContextMenu, new wx.EventListener(OnHistoryContextMenu));
			EVT_MENU((int) Id.showDescriptionContextMenu, new wx.EventListener(OnShowDescriptionContextMenu));
			// main menu
			EVT_MENU((int) Id.settingsMenu, new wx.EventListener(OnSettingsMenu));
			EVT_MENU((int) Id.historyMenu, new wx.EventListener(OnHistoryMenu));
			EVT_MENU(wx.Window.wxID_ABOUT, new wx.EventListener(OnAboutMenu));
			EVT_MENU((int) Id.newContactMenu, new wx.EventListener(OnNewContactMenu));
			EVT_MENU((int) Id.newAccountMenu, new wx.EventListener(OnNewAccountMenu));
			EVT_MENU((int) Id.changeEmailMenu, new wx.EventListener(OnChangeEmailMenu));
			EVT_MENU((int) Id.changePasswordMenu, new wx.EventListener(OnChangePasswordMenu));
			EVT_MENU((int) Id.deleteAccountMenu, new wx.EventListener(OnDeleteAccountMenu));
			EVT_MENU((int) Id.closeMenu, new wx.EventListener(OnCloseMenu));
			// Those events are called when we need to update widget from 
			// thread diffrent then main thread. To prevent calling this method on normal UI updates,
			// we use fake ID, so method will be called only when we need it.
			EVT_UPDATE_UI((int) FakeId.statusBitmap, new wx.EventListener(OnStatusBitmapUpdate));
			EVT_UPDATE_UI((int) FakeId.contactListCtrl, new wx.EventListener(OnContactListCtrlUpdate));
			EVT_UPDATE_UI((int) FakeId.contactAdded, new wx.EventListener(OnContactAdded));
			EVT_UPDATE_UI((int) FakeId.contactListClear, new wx.EventListener(OnContactListClear));
			EVT_UPDATE_UI((int) FakeId.clearDescriptionCombo, new wx.EventListener(bottomWindow.ClearDescriptionCombo));
			EVT_UPDATE_UI((int) FakeId.connectionTimeoutMessageDialog, new wx.EventListener(OnConnectionTimeoutDialog));
			EVT_UPDATE_UI((int) FakeId.emptyGaduSettingsMessageDialog, new wx.EventListener(EmptyGaduSettingsMessageDialog));
			EVT_UPDATE_UI((int) FakeId.deleteDescriptionsFileDialog, new wx.EventListener(OnDeleteDescriptionsFileDialog));
			EVT_UPDATE_UI((int) FakeId.setTrayIconBitmap, new wx.EventListener(SetTrayIconBitmap));
			EVT_UPDATE_UI((int) FakeId.showNewChatNotify, new wx.EventListener(ShowNewChatNotifyWindow));
			EVT_UPDATE_UI((int) FakeId.showStatusChangeNotify, new wx.EventListener(ShowStatusChangeNotify));
			#endregion
			#region user settings change
			// updates controls UI properties after user changed UserConfig based on Fake Id
			EVT_UPDATE_UI((int) FakeId.contactListFontsChanged, new wx.EventListener(dispatcher.ContactProxy.UpdateContactListCtrlFonts));
			EVT_UPDATE_UI((int) FakeId.contactListColorChanged, new wx.EventListener(topWindow.UpdateContactListColor));
			EVT_UPDATE_UI((int) FakeId.infoPanelFontsChanged, new wx.EventListener(bottomWindow.UpdatePanelFonts));
			EVT_UPDATE_UI((int) FakeId.infoPanelColorChanged, new wx.EventListener(bottomWindow.UpdatePanelColors));
			EVT_UPDATE_UI((int) FakeId.tooltipSettingsChanged, new wx.EventListener(UpdateTooltipSettings));
			EVT_UPDATE_UI((int) FakeId.mainWindowSizeChanged, new wx.EventListener(UpdateMainWindowSize));
			EVT_UPDATE_UI((int) FakeId.trayIconSettingsChanged, new wx.EventListener(UpdateTrayIcon));
			#endregion
			// subscribe for UserConfig change event,
			// this will use above EVT_UPDATE_UI event for calling method
			// which do _real_ control properties update
			appCfg.UserConfigChanged += new UserConfigChangedEventHandler(topWindow.ContactListFontChanged);
			appCfg.UserConfigChanged += new UserConfigChangedEventHandler(topWindow.ContactListColorChanged);
			appCfg.UserConfigChanged += new UserConfigChangedEventHandler(bottomWindow.PanelFontChanged);
			appCfg.UserConfigChanged += new UserConfigChangedEventHandler(bottomWindow.PanelColorChanged);
			appCfg.UserConfigChanged += new UserConfigChangedEventHandler(dispatcher.DescriptionSettingsChanged);
			appCfg.UserConfigChanged += new UserConfigChangedEventHandler(TooltipSettingsChanged);
			appCfg.UserConfigChanged += new UserConfigChangedEventHandler(dispatcher.LoggingSettingsChanged);		
			appCfg.UserConfigChanged += new UserConfigChangedEventHandler(MainWindowSizeChanged);
			appCfg.UserConfigChanged += new UserConfigChangedEventHandler(TrayIconSettingsChanged);
			
			// check user settings and setup default status
			switch (appCfg.UserConf.DefaultStatus)
			{
				case GGStatusType.Available:
					LaunchStatusChangeEvent(StatusConstans.online);
					break;
				case GGStatusType.Busy:
					LaunchStatusChangeEvent(StatusConstans.away);
					break;
				case GGStatusType.Invisible:
					LaunchStatusChangeEvent(StatusConstans.invisible);
					break;
				default:
					break;
			}
			
			// set main window desired size
			// this must be done outside of ctor
			// so we use UPDATE_UI event here
			MainWindowSizeChanged(this);	
		}

		private void DoControls()
		{
			splitterWindow = new wx.SplitterWindow(this, (int) Id.splitterWindow, wx.Window.wxDefaultPosition,
			                                       wx.Window.wxDefaultSize, wx.SplitterWindow.wxSP_LIVE_UPDATE);
			topWindow = new TopMainFramePanel(splitterWindow, this);
			bottomWindow = new BottomMainFramePanel(splitterWindow, this);
			
			#region create application menu
			menuBar = new wx.MenuBar();
			gaduMenu = new wx.Menu();
			toolsMenu = new wx.Menu();
			helpMenu = new wx.Menu();
			
			wx.MenuItem newContact = new wx.MenuItem(gaduMenu, (int) Id.newContactMenu, Catalog.GetString("New contact"));
			newContact.Bitmap = imageManager[ImageManager.NEW_CONTACT];
			gaduMenu.Append(newContact);
			gaduMenu.AppendSeparator();
			wx.MenuItem newAccount = new wx.MenuItem(gaduMenu, (int) Id.newAccountMenu, Catalog.GetString("New account"));
			newAccount.Bitmap = imageManager[ImageManager.NEW];
			gaduMenu.Append(newAccount);
			
			wx.MenuItem changeEmail = new wx.MenuItem(gaduMenu, (int) Id.changeEmailMenu, Catalog.GetString("Change e-mail address"));
			changeEmail.Bitmap = imageManager[ImageManager.MAIL];
			gaduMenu.Append(changeEmail);
			
			wx.MenuItem changePassword = new wx.MenuItem(gaduMenu, (int) Id.changePasswordMenu, Catalog.GetString("Change password"));
			changePassword.Bitmap = imageManager[ImageManager.PASSWORD];
			gaduMenu.Append(changePassword);
			
			wx.MenuItem deleteAccount = new wx.MenuItem(gaduMenu, (int) Id.deleteAccountMenu, Catalog.GetString("Delete account"));
			deleteAccount.Bitmap = imageManager[ImageManager.DELETE_CONTACT];
			gaduMenu.Append(deleteAccount);
			
			wx.MenuItem close = new wx.MenuItem(gaduMenu, (int) Id.closeMenu, Catalog.GetString("Exit"));
			close.Bitmap = imageManager[ImageManager.EXIT];
			gaduMenu.AppendSeparator();
			gaduMenu.Append(close);
			
			wx.MenuItem history = new wx.MenuItem(toolsMenu, (int) Id.historyMenu, Catalog.GetString("History browser"));
			history.Bitmap = imageManager[ImageManager.HISTORY];
			toolsMenu.Append(history);
			toolsMenu.AppendSeparator();
			wx.MenuItem settings = new wx.MenuItem(toolsMenu, (int) Id.settingsMenu, Catalog.GetString("Settings"));
			settings.Bitmap = imageManager[ImageManager.SETTINGS];
			toolsMenu.Append(settings);
			
			helpMenu.Append(wx.Window.wxID_ABOUT, Catalog.GetString("About"));
		
			menuBar.Append(gaduMenu, Catalog.GetString("Gadu"));
			menuBar.Append(toolsMenu, Catalog.GetString("Tools"));
			menuBar.Append(helpMenu, Catalog.GetString("Help"));
		
			this.MenuBar = menuBar;		
			#endregion
		}

		private void DoControlsProperties()
		{
			splitterWindow.SplitHorizontally(topWindow, bottomWindow);
			splitterWindow.SashGravity = 1.0;
			// apply tooltips user settings
			TooltipSettingsChanged(this);
			
			wx.Icon appLogo = new wx.Icon();
			appLogo.CopyFromBitmap(imageManager[ImageManager.APP_LOGO]);
			this.Icon = appLogo;
		}
		
		private void DoLayout()
		{
			topSizer = new wx.BoxSizer(wx.Orientation.wxVERTICAL);
			{
				topSizer.Add(splitterWindow, 1, wx.Stretch.wxEXPAND|wx.Direction.wxALL, 0);
			}
			this.Sizer = topSizer;
			topSizer.SetSizeHints(this);
			topSizer.Fit(this);
		}

		/// <summary>
		/// Filling contact list with contacts upon application start.
		/// </summary>
		/// <remarks>
		/// It's important to run this method after Gadu.Users list is filled in and
		/// before client connect to GG network.
		/// </remarks>
		/// <param name="sender">
		/// A <see cref="System.Object"/>
		/// </param>
		/// <param name="contactList">
		/// A <see cref="Dictionary"/>
		/// </param>
		public void LoadContactList(GGUsers contacts)
		{
			//clear whole list
			topWindow.ContactsListCtrl.DeleteAllItems();
			
			//insert contacts
			int i = 0;
			foreach (GGUser user in contacts)
			{
				if (user.Description != String.Empty)
				{
					topWindow.ContactsListCtrl.InsertItem(i++, 
					                    String.Format("{0} : {1}", user.GGNick, user.Description),
					                    ImageManager.ImageIndexFromStatus(user.GGStatus, user.Description));
				}
				else
				{
					topWindow.ContactsListCtrl.InsertItem(i++, 
					                    String.Format("{0}", user.GGNick),
					                    ImageManager.ImageIndexFromStatus(user.GGStatus, String.Empty));
				}
				
			}
		}

		public void LoadContactList(Dictionary<int, ContactInfo> contacts)
		{
			//clear whole list
			topWindow.ContactsListCtrl.DeleteAllItems();

			int i = 0;
			foreach (ContactInfo ci in contacts.Values)
			{
				ci.Position = i;
				topWindow.ContactsListCtrl.InsertItem(i++, 
				                    String.Format("{0} : \"{1}\"", ci.Name, ci.Description),
				                    ImageManager.ImageIndexFromStatus(GGStatusType.NotAvailable, String.Empty));
			}
		}

		public void LoadContactList(List<ContactInfo> contacts)
		{
			//clear whole list
			topWindow.ContactsListCtrl.DeleteAllItems();

			int i = 0;
			foreach (ContactInfo ci in contacts)
			{
				ci.Position = i;
				topWindow.ContactsListCtrl.InsertItem(i++, 
				                    String.Format("{0}", ci.Name),
				                    ci.Status);
			}
		}


		//// <value>
		/// <c>ImageManager</c> class object which
		/// holds collections of application images.
		/// </value>
		public ImageManager ImageMgr
		{
			get { return imageManager; }
		}

		public void LaunchStatusChangeEvent(int desiredStatus)
		{
			if (UserStatusChange != null)
				UserStatusChange(desiredStatus);
		}

		private void OnMainFrameClose(object sender, wx.Event evt)
		{
			if (realClose == false && trayIcon != null && appCfg.UserConf.CloseToTray == true)
			{
				this.Show(false);
			}
			else
			{
				wx.MessageDialog md;
				
				if (appCfg.UserConf.ConfirmExit)
				{
					md = new wx.MessageDialog(this, 
				                                           string.Format(
				                                                         Catalog.GetString("Do you realy want to close wxGadu?")),
				                                                         Catalog.GetString("Close?"),
			                                           wx.MessageDialog.wxYES_NO|wx.MessageDialog.wxICON_QUESTION);
					if (md.ShowModal() == wx.Window.wxID_NO)
						return;
				}
					
				// save main window size
				appCfg.UserConf.MainWindowSize = this.Size;
				// save main window position 
				appCfg.UserConf.MainWindowPosition = this.Position;
				
				trayIconMutex.WaitOne();
				{
					if (trayIcon is GaduTaskBarIcon)
					{
						trayIcon.RemoveIcon();
						trayIcon = null;
					}
				}
				trayIconMutex.ReleaseMutex();
				
				// save user config
				try 
				{
					appCfg.SaveUserConfig();
				}
				catch (Exception e)
				{
	#if LOG
					logger.Error(String.Format(Catalog.GetString("Unable to save user configuration settings into: {0}. {1}"), appCfg.ConfigFile, e.Message));
	#endif
					md = new wx.MessageDialog(this, 
					                                           string.Format(
					                                                         Catalog.GetString("Unable to save user configuration settings to: {0}.\n{1}"), appCfg.ConfigFile, e.Message),
					                                                         Catalog.GetString("Error"),
				                                           wx.MessageDialog.wxOK|wx.MessageDialog.wxICON_EXCLAMATION);
					md.ShowModal(); 
				}	
			
				dispatcher.Destroy();
	
				evt.Skip();
			}
		}
		
		/// <summary>
		/// Performs real close of <c>MainFrame</c>
		/// window.
		/// </summary>
		public void PerformRealClose()
		{
			realClose = true;
			this.Close();
		}


		//// <value>
		/// Bitmap that should be set as bottomWindow.StatusBitmap.Bitmap
		/// when user status was chaged and icon need to be updated
		/// </value>
		public wx.Bitmap StatusBitmapIcon
		{
			set {  statusBitmapIcon = value;	}
		}

		//// <value>
		/// Bottom part of MainFrame splitted window
		/// </value>
		public BottomMainFramePanel BottomWindow
		{
			get { return bottomWindow; }
		}

		//// <value>
		/// Top part of MainFrame splitted window
		/// </value>
		public TopMainFramePanel TopWindow
		{
			get { return topWindow; }
		}
		
		//// <value>
		/// Contact UIN which status should be updated
		/// on ContactListCtrl during UpdateUI event handling
		/// </value>
		public Queue<GGUser> OutdatedUsers
		{
			get { return outdatedUsers; }
		}
		
		public Queue<GGUser> AddedUsers
		{
			get { return addedUsers; }
		}

		/// <summary>
		/// This method is called when we change widget property
		/// form thread other than main (GUI) thread. This method
		/// should be called as response to EVT_UPDATE_UI with some
		/// fake widget ID.
		/// By doing this we can prevent UI lockup.
		/// </summary>
		/// <param name="sender">
		/// A <see cref="System.Object"/>
		/// </param>
		/// <param name="evt">
		/// A <see cref="wx.Event"/>
		/// </param>
		private void OnStatusBitmapUpdate(object sender, wx.Event evt)
		{
			wx.Bitmap newBitmap = imageManager.StatusBitmaps[ImageManager.ImageIndexFromStatus(dispatcher.Gadu.GGStatus, dispatcher.Gadu.GGDescription)];
			bottomWindow.StatusBitmap.Bitmap = newBitmap;	
		}
		

		
		private void OnContactListCtrlUpdate(object sender, wx.Event evt)
		{				
			dispatcher.ContactStatusMutex.WaitOne();
			
			GGUser user;
			while (outdatedUsers.Count > 0) 
			{
				user = outdatedUsers.Dequeue();	
				
#if LOG 
				if (dispatcher.ContactProxy.Count < dispatcher.Gadu.Users.Count)
					logger.Debug(Catalog.GetString("DEBUG: dispatcher.ContactProxy.Count < dispatcher.Gadu.Users.Count"));
#endif

				int newPos = dispatcher.Gadu.Users.IndexOf(user);		
				dispatcher.ContactProxy.UpdateContact(newPos, user);
					
			}
			dispatcher.ContactStatusMutex.ReleaseMutex();
		}
		
		private void OnContactAdded(object sender, wx.Event evt)
		{
			dispatcher.ContactStatusMutex.WaitOne();
			
			GGUser user;
			while (addedUsers.Count > 0) 
			{
				user = addedUsers.Dequeue();		
							
				dispatcher.ContactProxy.AddContact(user);
			}
			
			dispatcher.ContactStatusMutex.ReleaseMutex();
		}
		
		private void OnContactListClear(object sender, wx.Event evt)
		{
#if LOG
			logger.Trace(Catalog.GetString("Purging ContactListProxy and ContactListCtrl"));
#endif
			dispatcher.ContactProxy.ClearList();
		}
		
				
		public void ContactSelected(int realPosition)
		{
			ContactListProxy.UserPos contactPos = dispatcher.ContactProxy.GetUserPos(realPosition);
			// store contact uin for context menu event handler methods
			contexMenuUin = contactPos.uin;	
			
			topWindow.ContactsListCtrl.SetItemState(contactPos.main, wx.ListCtrl.STATE.SELECTED|wx.ListCtrl.STATE.FOCUSED, wx.ListCtrl.STATE.SELECTED|wx.ListCtrl.STATE.FOCUSED);
			//TODO: when we select description here this will cause calling this function again 
			// because selecting item in programmatic way processes ITEM_SELECTED event as well
			// as a result the same info will be displayed on info panel one more (spare) time
			if (contactPos.desc)
				TopWindow.ContactsListCtrl.SetItemState(contactPos.main+1, wx.ListCtrl.STATE.SELECTED, wx.ListCtrl.STATE.SELECTED);
			
			// display contact information on bottom window panel
			ContactInfo ci = dispatcher.ContactMgr.Contacts[contactPos.uin];
			bottomWindow.DisplayContactInfo(ci.Uin);
		}
		
		/// <summary>
		/// Invoke context menu corresponding to clicked contact
		/// </summary>
		/// <param name="realPosition">
		/// Real position of clicked item on <c>ContactListCtrl</c>
		/// A <see cref="System.Int32"/>
		/// </param>
		public void ContactRightClicked(int realPosition)
		{
			ContactListProxy.UserPos user = dispatcher.ContactProxy.GetUserPos(realPosition);
			// store contact uin for context menu event handler methods
			contexMenuUin = user.uin;			
			
			#if LOG
				logger.Trace(String.Format(Catalog.GetString("Showing context menu for UIN: {0} @ {1} real position"), user.uin, realPosition));		
			#endif
			
			if (listContexMenu == null)
			{
				listContexMenu = new wx.Menu();
				
				wx.MenuItem newChat = new wx.MenuItem(listContexMenu, (int) Id.newChatContextMenu, Catalog.GetString("New chat"));
				newChat.Bitmap = imageManager[ImageManager.NEW_CHAT];
				//newChat.Bitmap = 
				wx.MenuItem contactInfo = new wx.MenuItem(listContexMenu, (int) Id.contactInfoContextMenu, Catalog.GetString("Edit contact data"));
				contactInfo.Bitmap = imageManager[ImageManager.CONTACT_INFO];
				wx.MenuItem chatHistory = new wx.MenuItem(listContexMenu, (int) Id.chatHistoryContextMenu, Catalog.GetString("Chat history"));
				chatHistory.Bitmap = imageManager[ImageManager.HISTORY];
				wx.MenuItem contactDelete = new wx.MenuItem(listContexMenu, (int) Id.contactDeleteContextMenu, Catalog.GetString("Delete contact"));
				contactDelete.Bitmap = imageManager[ImageManager.DELETE_CONTACT];
				wx.MenuItem showDesc = new wx.MenuItem(listContexMenu, (int) Id.showDescriptionContextMenu, Catalog.GetString("Show description"));
				showDesc.Checkable = true;
				showDesc.Checked = true;
				
				listContexMenu.Append(newChat);
				listContexMenu.Append(contactInfo);
				listContexMenu.AppendSeparator();
				listContexMenu.Append(contactDelete);
				listContexMenu.AppendSeparator();
				listContexMenu.Append(chatHistory);
				listContexMenu.Append(showDesc);
			}
			
			this.PopupMenu(listContexMenu);
		}
		
		public void OnNewChatContextMenu(object sender, wx.Event evt)
		{
			if (dispatcher.Gadu.IsGGLogged)	
				dispatcher.CreateNewChat(this.contexMenuUin);
			else
			{
				wx.MessageDialog md = new wx.MessageDialog(this, Catalog.GetString("You must be connected to GG network to do that"), 
				                                           Catalog.GetString("Not connected"),
			                                           wx.MessageDialog.wxOK|wx.MessageDialog.wxICON_EXCLAMATION);
				md.ShowModal();    
			}
		}
		
		protected void OnContactInfoContextMenu(object sender, wx.Event evt)
		{
			ContactInfo contactData = dispatcher.ContactMgr.Contacts[this.contexMenuUin];
			// if window is open bring focus to it, if window is not open open it and add to opened window 
			if (openedContactInfos.ContainsKey(contactData.Uin))
			{
				openedContactInfos[contactData.Uin].Show(true);
				openedContactInfos[contactData.Uin].SetFocus();
			}
			else
			{
				logger.Trace(String.Format(Catalog.GetString("Opening contact info dialog for UIN: {0}"), contactData.Uin));
				ContactInfoDialog contactDlg = new ContactInfoDialog(this, 
				                                                  wx.Window.wxID_ANY,
				                                                  contactData.Name,
				                                                  wx.Window.wxDefaultPosition,
				                                                  wx.Window.wxDefaultSize,
																	wx.Dialog.wxSYSTEM_MENU|
				                                                    wx.Dialog.wxCAPTION|
				                                                    wx.Dialog.wxRESIZE_BORDER| 
																	wx.Dialog.wxMINIMIZE_BOX|
																	wx.Dialog.wxMAXIMIZE_BOX|
																	wx.Dialog.wxCLOSE_BOX,
				                                                  contactData);		 
				contactDlg.Show(true);
				
				openedContactInfos.Add(contactData.Uin, contactDlg);
			}
		}
		
		
		protected void OnContactDeleteContextMenu(object sender, wx.Event evt)
		{
			ContactInfo contactData = dispatcher.ContactMgr.Contacts[this.contexMenuUin];
			
			wx.MessageDialog md = new wx.MessageDialog(this, 
			                                           string.Format(Catalog.GetString("Do you realy want to delete: {0} [{1}]?"), contactData.Name, contactData.Uin), 
			                                           Catalog.GetString("Delete contact"),
			                                           wx.MessageDialog.wxYES_NO|wx.MessageDialog.wxICON_QUESTION|wx.MessageDialog.wxNO_DEFAULT);
			if (md.ShowModal() == wx.Window.wxID_NO)
				return;
			
#if LOG
			logger.Trace(string.Format(Catalog.GetString("Deleting contact: {0} [{1}]?"), contactData.Name, contactData.Uin));
#endif
			
			// first remove contact from Gadu.Users subscription list
			dispatcher.GaduUsersMutex.WaitOne();
			{
				if (!dispatcher.Gadu.Users.Contains(contactData.Uin))
				{
#if LOG
					logger.Debug(String.Format(
					          Catalog.GetString("Gadu.Users list doesn't contain UIN: {0}, if you are NOT connected to GG newtwork this is normal, otherwise this is a bug"),
					                           contactData.Uin));
#endif
#if DEBUG
					if (dispatcher.Gadu.IsGGLogged)
						throw new ApplicationException(String.Format(
					          Catalog.GetString("Gadu.Users list doesn't contain UIN: {0}, if you are NOT connected to GG newtwork this is normal, otherwise this is a bug"),
					                           contactData.Uin));
#endif		                               
				}
				else
				{
					dispatcher.Gadu.Users.Remove(contactData.Uin);
				}
			}
			dispatcher.GaduUsersMutex.ReleaseMutex();
			
			// remove contact from status tracking list 
			if (appCfg.UserConf.TrackedContacts.Contains(contactData.Uin))
			{
#if LOG
				logger.Trace(string.Format(Catalog.GetString("Removing UIN: {0} from status tracking list"), contactData.Uin));
#endif
				appCfg.UserConf.TrackedContacts.Remove(contactData.Uin);
			}
			
			// remove contact from ContactListProxy and ContactListCtrl
#if LOG
			logger.Trace(string.Format(Catalog.GetString("Removing UIN: {0} from contact list ctrl list"), contactData.Uin));
#endif
			dispatcher.ContactProxy.DeleteContact(contactData.Uin);
			
			// remove contact from ContactManager and contacts file
#if LOG
			logger.Trace(string.Format(Catalog.GetString("Removing UIN: {0} from contacts file"), contactData.Uin));
#endif
			dispatcher.ContactMgr.DeleteContact(contactData.Uin);
			
			// clear InfoPanel
			bottomWindow.ClearInfoPanel();
			
			//TODO: remove contact history (when it will be done)
			
		}
		
		protected void OnHistoryContextMenu(object sender, wx.Event evt)
		{
			ContactInfo contactData = dispatcher.ContactMgr.Contacts[this.contexMenuUin];
			
			if (histFrame != null)
			{
				histFrame.ExpandContactHistory(contactData.Uin);
				histFrame.Raise();
			}
			else
			{
				histFrame = new HistoryFrame(this, -1, Catalog.GetString("History browser"),
				                                          wx.Window.wxDefaultPosition, wx.Window.wxDefaultSize,
				                                          wx.Frame.wxDEFAULT_FRAME);
				histFrame.Show();
				histFrame.Raise();
				histFrame.ExpandContactHistory(contactData.Uin);
			}
			
			
		}
		
		protected void OnShowDescriptionContextMenu(object sender, wx.Event evt)
		{
			wx.MessageDialog md = new wx.MessageDialog(this, string.Format("This function is not ready yet"), "Function not implemented",
			                                           wx.MessageDialog.wxOK|wx.MessageDialog.wxICON_EXCLAMATION);
			md.ShowModal();    
		}
		
		/// <summary>
		/// Deletes <c>ContactInfoDialog</c> window handler
		/// specified by <c>uin</c> from
		/// <c>openedContactInfos</c>
		/// </summary>
		/// <remarks>
		/// Each opened <c>ContactInfoDialog</c> window should
		/// call this method on destroy.
		/// </remarks>
		/// <param name="uin">
		/// A <see cref="System.Int32"/>
		/// UIN of contact behalf on which <c>ContactInfoDialog</c>
		/// was invoked.
		/// </param>
		public void DeleteContactInfoDialog(int uin)
		{
			if (openedContactInfos.ContainsKey(uin))
			{
				openedContactInfos.Remove(uin);
			}
		}
		
		public Dispatcher Dispatch
		{
			get { return dispatcher; }
		}
		
		public AppConfig AppCfg
		{
			get { return appCfg; }
		}
		
		private void OnSettingsMenu(object sender, wx.Event evt)
		{
			ConfigurationDialog configDlg = new ConfigurationDialog(this, -1, Catalog.GetString("Settings"), 
			                                                        wx.Window.wxDefaultPosition, wx.Window.wxDefaultSize,
			                                                        wx.Dialog.wxDEFAULT_DIALOG_STYLE|wx.Dialog.wxRESIZE_BORDER
			                                                        );
			configDlg.ShowModal();
		}
		
		private void OnHistoryMenu(object sender, wx.Event evt)
		{
			if (histFrame == null)
			{
				histFrame = new HistoryFrame(this, -1, Catalog.GetString("History browser"),
				                                          wx.Window.wxDefaultPosition, wx.Window.wxDefaultSize,
				                                          wx.Frame.wxDEFAULT_FRAME);
				histFrame.Show();
			}
			else
			{
				histFrame.Raise();
			}
		}
				
		private void OnNewContactMenu(object sender, wx.Event evt)
		{
			ContactAddDialog contactAddDlg = new ContactAddDialog(this, wx.Window.wxID_ANY, 
			                                                      Catalog.GetString("Add new contact"),
			                                                      wx.Window.wxDefaultPosition, wx.Window.wxDefaultSize,
			                                                      wx.Dialog.wxDEFAULT_DIALOG_STYLE);
			
			contactAddDlg.Show(true);
		}
		
		private void OnNewAccountMenu(object sender, wx.Event evt)
		{
			NewAccountDialog newAccountDlg = new NewAccountDialog(this, wx.Window.wxID_ANY, 
			                                                      Catalog.GetString("Create new Gadu-Gadu account"),
			                                                      wx.Window.wxDefaultPosition, wx.Window.wxDefaultSize,
			                                                      wx.Dialog.wxDEFAULT_DIALOG_STYLE);
			newAccountDlg.Show(true);
		}
		
		private void OnChangeEmailMenu(object sender, wx.Event evt)
		{
			ChangeEmailDialog changeEmailDlg = new ChangeEmailDialog(this, wx.Window.wxID_ANY, 
			                                                      Catalog.GetString("Change e-mail address"),
			                                                      wx.Window.wxDefaultPosition, wx.Window.wxDefaultSize,
			                                                      wx.Dialog.wxDEFAULT_DIALOG_STYLE);
			changeEmailDlg.Show(true);
		}
		
		private void OnChangePasswordMenu(object sender, wx.Event evt)
		{
			ChangePasswordDialog changePasswordDlg = new ChangePasswordDialog(this, wx.Window.wxID_ANY, 
			                                                      Catalog.GetString("Change account password"),
			                                                      wx.Window.wxDefaultPosition, wx.Window.wxDefaultSize,
			                                                      wx.Dialog.wxDEFAULT_DIALOG_STYLE);
			changePasswordDlg.ShowModal();
		}
		
		private void OnDeleteAccountMenu(object sender, wx.Event evt)
		{
			if (dispatcher.Gadu.IsGGLogged)
			{
				wx.MessageDialog md = new wx.MessageDialog(this, Catalog.GetString("You must disconnect from GG network first"), 
				                          Catalog.GetString("Error"), wx.MessageDialog.wxICON_ERROR|
				                          wx.MessageDialog.wxOK);
				md.ShowModal();
				return;
			}
			
			DeleteAccountDialog deleteAccountDlg = new DeleteAccountDialog(this, wx.Window.wxID_ANY, 
			                                                      Catalog.GetString("Delete account (UIN)"),
			                                                      wx.Window.wxDefaultPosition, wx.Window.wxDefaultSize,
			                                                      wx.Dialog.wxDEFAULT_DIALOG_STYLE);
			deleteAccountDlg.ShowModal();
		}
				
		private void OnCloseMenu(object sender, wx.Event evt)
		{
			this.PerformRealClose();
		}
		
		private void OnAboutMenu(object sender, wx.Event evt)
		{
			AboutDialog aboutDlg = new AboutDialog(this);
			aboutDlg.ShowModal();
		}
		
		private void OnConnectionTimeoutDialog(object sender, wx.Event evt)
		{
			wx.MessageDialog md = new wx.MessageDialog(this, Catalog.GetString("Connection to GG server timeout. Try using different server or/and increasing connection timeout limit."), 
					Catalog.GetString("Connection timeout"),
					wx.MessageDialog.wxOK|wx.MessageDialog.wxICON_EXCLAMATION);
			md.ShowModal();
		}
		
		private void EmptyGaduSettingsMessageDialog(object sender, wx.Event evt)
		{
			wx.MessageDialog md = new wx.MessageDialog(this, Catalog.GetString("You must set GG number and password before you can connect."), 
					Catalog.GetString("Empty Gadu settings"),
					wx.MessageDialog.wxOK|wx.MessageDialog.wxICON_EXCLAMATION);
			md.ShowModal();
		}
		
		public void HistoryErrorMessageDialog(string infoMessage)
		{
			wx.MessageDialog md = new wx.MessageDialog(this, infoMessage, 
					Catalog.GetString("History error"),
					wx.MessageDialog.wxOK|wx.MessageDialog.wxICON_ERROR);
			md.ShowModal();
		}
		
		private void OnDeleteDescriptionsFileDialog(object sender, wx.Event evt)
		{
			wx.MessageDialog md = new wx.MessageDialog(this, Catalog.GetString("Would you like to delete descriptions file?"), 
					Catalog.GetString("Delete file"),
					wx.MessageDialog.wxYES_NO|wx.MessageDialog.wxICON_QUESTION);
			
			if (md.ShowModal() == wx.Window.wxID_YES)
			{
				if (File.Exists(AppCfg.DescriptionsFile))
				{
					try
					{
						File.Delete(AppCfg.DescriptionsFile);
					}
					catch (Exception e)
					{
#if LOG
						logger.Error(String.Format(Catalog.GetString("Unable to delete description file: {0}. {1}"), AppCfg.DescriptionsFile, e.Message));
#endif
						md = new wx.MessageDialog(this, String.Format(Catalog.GetString("Unable to delete description file: {0}. {1}"), AppCfg.DescriptionsFile, e.Message),
								Catalog.GetString("Error"), 
						        wx.MessageDialog.wxICON_ERROR|wx.MessageDialog.wxOK);
						md.ShowModal();
					}
				}
			}
		}
		
		private void UpdateTooltipSettings(object sender, wx.Event evt)
		{
			wx.ToolTip.Enabled = appCfg.UserConf.ShowTooltips;
			wx.ToolTip.Delay = appCfg.UserConf.TooltipsDelay;
		}
		
		private void TooltipSettingsChanged(object sender)
		{
			this.EventHandler.AddPendingEvent(new wx.UpdateUIEvent((int) FakeId.tooltipSettingsChanged));
		}
		
		private void MainWindowSizeChanged(object sender)
		{
			this.EventHandler.AddPendingEvent(new wx.UpdateUIEvent((int) FakeId.mainWindowSizeChanged));
		}
		
		private void TrayIconSettingsChanged(object sender)
		{
			this.EventHandler.AddPendingEvent(new wx.UpdateUIEvent((int) FakeId.trayIconSettingsChanged));
		}
		
		private void UpdateMainWindowSize(object sender, wx.Event evt)
		{
			this.Size = appCfg.UserConf.MainWindowSize;
		}
		
		private void UpdateTrayIcon(object sender, wx.Event evt)
		{
			trayIconMutex.WaitOne();
			{
				if (appCfg.UserConf.EnableTrayIcon)
				{
					if (!(trayIcon is GaduTaskBarIcon))
					{
						trayIcon = new GaduTaskBarIcon(this);
						this.EventHandler.AddPendingEvent(new wx.UpdateUIEvent((int) FakeId.setTrayIconBitmap));
					}
				}
				else
				{
					if (trayIcon is GaduTaskBarIcon)
					{
						trayIcon.RemoveIcon();
						trayIcon = null;
					}
				}
			}
			trayIconMutex.ReleaseMutex();
		}
		
		public void SetTrayIconBitmap(object sender, wx.Event evt)
		{
#if LOG
			logger.Trace(Catalog.GetString("Setting tray icon"));
#endif
			trayIconMutex.WaitOne();
			{
				if (trayIcon == null)
				{
					trayIconMutex.ReleaseMutex();
					return;
				}
				
				wx.Icon icon = new wx.Icon();
				string tooltip;
				// check if there is something on incoming chats list
				if (dispatcher.IncomingChats.Count == 0)
				{
					int status = StatusConstans.FromGGStatus(dispatcher.Gadu.GGStatus);
					icon.CopyFromBitmap(imageManager.StatusBitmaps[status]);
					tooltip = ContactManager.StatusToString(status);
				}
				else
				{
					
					icon.CopyFromBitmap(imageManager[ImageManager.INCOMING_CHAT]);
					tooltip = Catalog.GetString("New chat");
				}
				
				trayIcon.SetIcon(icon, tooltip);
			}
			trayIconMutex.ReleaseMutex();
		}
		
		
		private void ShowNewChatNotifyWindow(object sender, wx.Event evt)
		{
			dispatcher.ChatNotifyUinsMutex.WaitOne();
			{
				int uin;
				while (dispatcher.ChatNotifyUins.Count > 0)
				{
					uin = dispatcher.ChatNotifyUins.Dequeue();
					
					// if caller uin is on our contact list
					if (dispatcher.ContactMgr.Contacts.ContainsKey(uin))
					{
						ContactInfo callerInfo = dispatcher.ContactMgr.Contacts[uin];
						if (!(callerInfo.Photo is wx.Bitmap))
						{
							string path = appCfg.AvatarsDir + Path.DirectorySeparatorChar + callerInfo.Uin + ".jpg";
							if (File.Exists(path))
							{
								try
								{
									wx.Bitmap avatar = new wx.Bitmap(Helper.LoadAndScale(path, 96));
									callerInfo.Photo = avatar;
								}
								catch (ArgumentException)
								{ 
									callerInfo.Photo = new wx.Bitmap(Helper.LoadAndScale(imageManager[ImageManager.NOBODY].ConvertToImage() , 96));
								}
							}
							else
							{
								callerInfo.Photo = new wx.Bitmap(Helper.LoadAndScale(imageManager[ImageManager.NOBODY].ConvertToImage() , 96));
							}
						}
						
						wx.Display disp = wx.Display.GetDisplay(0);

						NotifyWindow newChatNotify = new NotifyWindow(this, wx.Window.wxID_ANY,
						                                              Catalog.GetString("[New chat]"),
						                                              callerInfo.Name,
						                                              callerInfo.Photo,
						                                              appCfg.UserConf.NotifyTtl,
						                                              wx.Window.wxDefaultPosition);
						int xpos = disp.Geometry.Width - newChatNotify.Size.Width;
						int ypos = disp.Geometry.Height - newChatNotify.Size.Height;
						newChatNotify.Position = new Point(xpos >= 0 ? xpos : 0, ypos >= 0 ? ypos : 0);
						newChatNotify.Show(true);
						
					}
					else	// caller is not on contact list, load nobody pixmap
					{
						wx.Display disp = wx.Display.GetDisplay(0);
						NotifyWindow newChatNotify = new NotifyWindow(this, wx.Window.wxID_ANY,
						                                              Catalog.GetString("[New chat]"),
						                                              uin.ToString(),
						                                              new wx.Bitmap(Helper.LoadAndScale(imageManager[ImageManager.NOBODY].ConvertToImage(), 96)),
						                                              appCfg.UserConf.NotifyTtl,
						                                              wx.Window.wxDefaultPosition);
						int xpos = disp.Geometry.Width - newChatNotify.Size.Width;
						int ypos = disp.Geometry.Height - newChatNotify.Size.Height;
						newChatNotify.Position = new Point(xpos >= 0 ? xpos : 0, ypos >= 0 ? ypos : 0);
						newChatNotify.Show(true);												
					}
				}			
			}
			dispatcher.ChatNotifyUinsMutex.ReleaseMutex();
		}
		
		
		private void ShowStatusChangeNotify(object sender, wx.Event evt)
		{
			statusChangNotifyMutex.WaitOne();
			{
				int uin = statusChangeNotifyData.uin;
					
				// if caller uin is on our contact list
				if (dispatcher.ContactMgr.Contacts.ContainsKey(uin))
				{
					ContactInfo callerInfo = dispatcher.ContactMgr.Contacts[uin];
					if (!(callerInfo.Photo is wx.Bitmap))
					{
						string path = appCfg.AvatarsDir + Path.DirectorySeparatorChar + callerInfo.Uin + ".jpg";
						if (File.Exists(path))
						{
							try
							{
								wx.Bitmap avatar = new wx.Bitmap(Helper.LoadAndScale(path, 96));
								callerInfo.Photo = avatar;
							}
							catch (ArgumentException)
							{ 
								callerInfo.Photo = new wx.Bitmap(Helper.LoadAndScale(imageManager[ImageManager.NOBODY].ConvertToImage() , 96));
							}
						}
						else
						{
							callerInfo.Photo = new wx.Bitmap(Helper.LoadAndScale(imageManager[ImageManager.NOBODY].ConvertToImage() , 96));
						}
					}
					
					wx.Display disp = wx.Display.GetDisplay(0);
					
					NotifyWindow newChatNotify = new NotifyWindow(this, wx.Window.wxID_ANY,
					                                              Catalog.GetString("[" + statusChangeNotifyData.status + "]"),
					                                              callerInfo.Name,
					                                              callerInfo.Photo,
					                                              appCfg.UserConf.NotifyTtl,
					                                              wx.Window.wxDefaultPosition);
					int xpos = disp.Geometry.Width - newChatNotify.Size.Width;
					int ypos = disp.Geometry.Height - newChatNotify.Size.Height;
					newChatNotify.Position = new Point(xpos >= 0 ? xpos : 0, ypos >= 0 ? ypos : 0);
					newChatNotify.Show(true);
				}
			}
			statusChangNotifyMutex.ReleaseMutex();
		}
		
//		public GaduTaskBarIcon TrayIcon
//		{
//			get { return trayIcon; }
//			set { trayIcon = null; }
//		}
		
		public ContactStatusChangeData StatusChangeNotifyData
		{
//			get { return statusChangeNotifyUin; }
			set { statusChangeNotifyData = value; }
		}
		
		public Mutex StatusChangNotifyMutex
		{
			get { return statusChangNotifyMutex; }
		}
		
		public HistoryFrame HistFrame
		{
			set { histFrame = value; }
		}
		
	}
}
